#include "TestCompresor.h"

#include "../src/compresor/Aritmetico.h"
#include "../src/Bytes.h"
#include "../src/FuncionesUtiles.h"
#include <cstdio>
#include <cstdlib>

TestCompresor::TestCompresor() {


}

TestCompresor::~TestCompresor() {

}

void TestCompresor::run() {
	calculoPisoTecho();
	overflow();
	underflow();
	nuevosPTOverflow();
	nuevosPTOverflow2();
	nuevosPTUnderflow();
	emisionOverflow();
	estoyEnPadding();
	moverVentana();
	moverABC();
	moverUnder();

	CASASOAS();
	Ab();
	AAA();
	biblia();
}

uint32 cambiarO(uint32 byte) {

	return (((0x000000ff & byte) << 24) | ((0xff000000 & byte) >> 24)
			| ((0x00ff0000 & byte) >> 8) | ((0x0000ff00 & byte) << 8));

}
void moverVentanaMaria(Bytes salida, Aritmetico a) {

	uint32 ventanaBits = 0;
	cantBytes contadorBytes = 3;
	Bytes bytes = salida;
	a.inicializarVentana(bytes, ventanaBits);
	char bitN = 8;

	std::cout << " la ventana inicial" << (uint32) cambiarO(ventanaBits)
			<< std::endl;

	a.moverVentanaNbits(ventanaBits, 8, bytes, contadorBytes, bitN,
			CASO_OVERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;

	a.moverVentanaNbits(ventanaBits, 8, bytes, contadorBytes, bitN,
			CASO_OVERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;

	a.moverVentanaNbits(ventanaBits, 8, bytes, contadorBytes, bitN,
			CASO_OVERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;

	a.moverVentanaNbits(ventanaBits, 8, bytes, contadorBytes, bitN,
			CASO_OVERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;

	a.moverVentanaNbits(ventanaBits, 5, bytes, contadorBytes, bitN,
			CASO_OVERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;
	a.moverVentanaNbits(ventanaBits, 1, bytes, contadorBytes, bitN,
			CASO_UNDERFLOW);
	std::cout << " muevo la ventana y queda " << (uint32) cambiarO(ventanaBits)
			<< std::endl;

}

void TestCompresor::calculoPisoTecho() {
	std::cout
			<< " --------------------Test de calculo de piso y techo del ultimo caracter del diccionario ----------------------"
			<< std::endl;
	Aritmetico aritmetico;
	uint32 piso;
	uint32 techo;
	uint32 noSeUsa;
	char c = 255;
	aritmetico.CalcularPisoTecho(c, piso, techo, COMPRIMIENDO, noSeUsa);

	assert(techo == 4294967295,
			"el techo del último caracter es 2^32-1 = 4294967295");
}

void TestCompresor::overflow() {
	std::cout
			<< " --------------------Test de overflow y su emision----------------------"
			<< std::endl;

	std::cout << "el tamaño de int32 es " << sizeof(uint32) << std::endl;
	uint32 salida = 0;
	char cantidad = 0;
	Aritmetico aritmetico;
	/*4204265471 = 11111010100101111111111111111111*/
	/*4204003329 = 11111010100101000000000000000001*/
	/*             11111010100101000000000000000000 =   4204003328 */
	cantidad = aritmetico.overflow(4204265471, 4204003329, salida);
	assert(salida == 4204003328, "la salida es la esperada");
	assert(cantidad == 14, "la cantidad es la esperada");

	/*2147483648 = 10.......0*/
	/*		   0 = 0.......0*/
	salida = 0;
	cantidad = aritmetico.overflow(2147483648, 0, salida);
	assert(salida == 2147483648, "la salida es la esperada");
	assert(cantidad == 0, "la cantidad es la esperada");

	/*2147483648 = 010.....0*/
	/*		   0 = 0.......0*/
	salida = 0;
	cantidad = aritmetico.overflow(1073741824, 0, salida);
	assert(salida == 0, "la salida es la esperada");
	assert(cantidad == 1, "la cantidad es la esperada");

	/*134217728 = 000010.....0*/
	/*		   0 = 0.......0*/
	salida = 0;
	cantidad = aritmetico.overflow(134217728, 0, salida);
	assert(salida == 0, "la salida es la esperada");
	assert(cantidad == 4, "la cantidad es la esperada");

	/*134217728 = 000010.....0*/
	/*		   0 = 0.......0*/
	salida = 0;
	cantidad = aritmetico.overflow(134217728, 0, salida);
	assert(salida == 0, "la salida es la esperada");
	assert(cantidad == 4, "la cantidad es la esperada");

	/*2936012800 = 101011110.....0*/
	/*	2885681152 = 10101100.......0*/
	/*salida = 1010110..00 = 2885681152*/
	salida = 0;
	cantidad = aritmetico.overflow(2936012800, 2885681152, salida);
	assert(salida == 2885681152, "la salida es la esperada");
	assert(cantidad == 6, "la cantidad es la esperada");

	/*2902403370 = 10101100111111110010100100101010*/
	/*	2885681152 = 101011000.......0*/
	salida = 0;
	cantidad = aritmetico.overflow(2902403370, 2885681152, salida);
	assert(salida == 2885681152, "la salida es la esperada");
	assert(cantidad == 8, "la cantidad es la esperada");
}

void TestCompresor::underflow() {
	std::cout
			<< " --------------------Test de underflow y su emision----------------------"
			<< std::endl;
	Aritmetico aritmetico;
	/*2190999551 = 10000010100101111111111111111111*/
	/*1922564095 = 01110010100101111111111111111111*/
	/*             -zzz  */
	char cantidad = aritmetico.underflow(2190999551, 1922564095);
	assert(cantidad == 3, "la salida es la esperada: U = 3");

	cantidad = aritmetico.underflow(4294967294, 0);
	assert(cantidad == 0, "la salida es la esperada: U = 0");

	cantidad = aritmetico.underflow(2902403370, 687810858);
	assert(cantidad == 0, "la salida es la esperada: U = 4");

	cantidad = aritmetico.underflow(2835349504, 687810858);
	assert(cantidad == 0, "la salida es la esperada: U = 6");
	cantidad = aritmetico.underflow(2826960896, 687810858);
	assert(cantidad == 0, "la salida es la esperada: U = 8");

	cantidad = aritmetico.underflow(2834245930, 687810858);
	assert(cantidad == 0, "la salida es la esperada: U = 10");

}

void TestCompresor::nuevosPTOverflow() {
	std::cout
			<< " --------------------Test de overflow: se calculan el techo y el piso----------------------"
			<< std::endl;
	Aritmetico aritmetico;
	uint32 techo = 4204265471;
	/*4204265471 = 11111010100101111111111111111111*/
	/*cant a leer = 14*/
	/*nuevo: 111111111111111111-11111111111111  = 4294967295*/

	aritmetico.nuevoTechoOver(techo, 14);
	assert(4294967295 == techo,
			"el techo con los unos al final es el correspondiente");

	uint32 piso = 4204003329;
	/*4204003329 = 11111010100101000000000000000001*/
	/*nuevo: 000000000000000001-00000000000000  = 16384*/
	aritmetico.nuevoPisoOver(piso, 14);
	assert(16384 == piso,
			"el piso con los ceros al final es el correspondiente");

}
//TODO HACER ASSERTS
void TestCompresor::nuevosPTOverflow2() {
	std::cout
			<< " --------------------Test de overflow 2: se calculan el techo y el piso----------------------"
			<< std::endl;
	Aritmetico aritmetico;
	uint32 techo = 1107296255;

	aritmetico.nuevoTechoOver(techo, 8);

	std::cout << "el techo con los unos al final es el correspondiente"
			<< (uint32) techo << endl;

	uint32 piso = 1090519040;

	aritmetico.nuevoPisoOver(piso, 8);
	std::cout << "el piso con los unos al final es el correspondiente"
			<< (uint32) piso << endl;

}

void TestCompresor::nuevosPTUnderflow() {
	std::cout
			<< " --------------------Test de underflow: se calculan el techo y el piso----------------------"
			<< std::endl;

	Aritmetico aritmetico;
	uint32 techo = 2190999551;
	/*2190999551 = 10000010100101111111111111111111*/
	/*1922564095 = 01110010100101111111111111111111*/
	/*             -zzz  */
	/*nuevoTecho: 1-0010100101111111111111111111-111  = 2495610879*/

	aritmetico.nuevoTechoUnder(techo, 3);
	assert(2495610879 == techo,
			"el techo con los unos al final es el correspondiente");

	uint32 piso = 1922564095;
	/*1922564095 = 01110010100101111111111111111111*/
	/*nuevopiso:   0-0010100101111111111111111111-000  = 348127224*/
	aritmetico.nuevoPisoUnder(piso, 3);
	assert(348127224 == piso,
			"el piso con los ceros al final es el correspondiente");

}

void TestCompresor::emisionOverflow() {
	std::cout
			<< " --------------------Test de emision overflow----------------------"
			<< std::endl;
	Aritmetico aritmetico;
	/*1912602624 = 01110010-000000000000000000000000*/
	uint32 bitsSalida = 1912602624;
	char cantBitsOver = 8;
	aritmetico.emitirOver(cantBitsOver, bitsSalida);
	//assert();
	//TODO hcer assert

}

void TestCompresor::estoyEnPadding() {

	std::cout
			<< " --------------------Test estoy en el padding----------------------"
			<< std::endl;
	Aritmetico a;
	Bytes b;
	unsigned char c = 0; //00000000
	b.agregarAlFinal(&c, 1);
	c = -1; //11111111
	b.agregarAlFinal(&c, 1);
	c = 8; //00001000
	b.agregarAlFinal(&c, 1);

	//Bytes 00000000 11111111 00001000

	cantBytes byteN = 0;
	char bitN = 0;
	//Bytes |00000000 11111111 00001000
	assert(!a.estoyEnElPadding(bitN, byteN, b), "No estoy en el padding");
	bitN = 6;
	byteN = 1;
	//Bytes 00000000 111111|11 00001000
	assert(!a.estoyEnElPadding(bitN, byteN, b), "No estoy en el padding");
	bitN = 8;
	byteN = 1;
	//Bytes 00000000 11111111| 00001000
	assert(!a.estoyEnElPadding(bitN, byteN, b), "No estoy en el padding");
	bitN = 1;
	byteN = 2;
	//Bytes 00000000 11111111 0|0001000
	assert(!a.estoyEnElPadding(bitN, byteN, b), "No estoy en el padding");
	bitN = 4;
	byteN = 2;
	//Bytes 00000000 11111111 0000|1000
	assert(a.estoyEnElPadding(bitN, byteN, b), "Estoy en el padding");
	bitN = 5;
	byteN = 2;
	//Bytes 00000000 11111111 00001|000
	assert(!a.estoyEnElPadding(bitN, byteN, b),
			"No estoy en el padding (ya me pase)");
	c = 0x80;//10000000
	b.agregarAlFinal(&c, 1);
	//Bytes 00000000 11111111 00001000 10000000
	bitN = 5;
	byteN = 1;
	//Bytes 00000000 11111|111 00001000 10000000
	assert(!a.estoyEnElPadding(bitN, byteN, b), "No estoy en el padding");
	bitN = 8;
	byteN = 2;
	//Bytes 00000000 11111111 00001000| 10000000
	assert(a.estoyEnElPadding(bitN, byteN, b), "Estoy en el padding");

}

void TestCompresor::moverVentana() {
	std::cout
			<< " --------------------Test mover ventana----------------------"
			<< std::endl;
	Aritmetico d;
	Bytes b;
	char bitN = 8;
	cantBytes byteN = 3;
	char c = 0x43;
	b.agregarAlFinal(&c, 1);
	c = 0x41;
	b.agregarAlFinal(&c, 1);
	c = 0x53;
	b.agregarAlFinal(&c, 1);
	c = 0x41;
	b.agregarAlFinal(&c, 1);
	c = 0x53;
	b.agregarAlFinal(&c, 1);
	c = 0x4c;
	b.agregarAlFinal(&c, 1);
	c = 0x95;
	b.agregarAlFinal(&c, 1);
	uint32 ventana = 0;
	b.leer(&ventana, 0, 4);
	//   ventana = cambiarO(ventana);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n", cambiarO(ventana));
	d.moverVentanaNbits(ventana, 8, b, byteN, bitN, CASO_OVERFLOW);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n\n\n", cambiarO(ventana));
	d.moverVentanaNbits(ventana, 8, b, byteN, bitN, CASO_OVERFLOW);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n\n\n", cambiarO(ventana));

	//    for(unsigned i = 4; i<b.getTamanio();i++){
	//    	printf("Byte %d: %#x \n",i,d.leerNbits(b,8,bitN,byteN));
	//    }


}

void TestCompresor::moverABC() {
	std::cout << " --------------------Test de mover ABC----------------------"
			<< std::endl;
	Aritmetico d;
	Bytes b;
	char bitN = 8;
	cantBytes byteN = 3;
	char c = 0x12;
	b.agregarAlFinal(&c, 1);
	c = 0x34;
	b.agregarAlFinal(&c, 1);
	c = 0x56;
	b.agregarAlFinal(&c, 1);
	c = 0x78;
	b.agregarAlFinal(&c, 1);
	c = 0x9A;
	b.agregarAlFinal(&c, 1);
	c = 0xbc;
	b.agregarAlFinal(&c, 1);
	c = 0xde;
	b.agregarAlFinal(&c, 1);
	uint32 ventana = 0;
	b.leer(&ventana, 0, 4);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n", cambiarO(ventana));
	d.moverVentanaNbits(ventana, 5, b, byteN, bitN, CASO_OVERFLOW);
	printf("ventana 5 : %#x \n", ventana);
	printf("ventanaC 5 : %#x \n", cambiarO(ventana));
}

void TestCompresor::moverUnder() {
	std::cout
			<< " --------------------Test MOVER underflow----------------------"
			<< std::endl;
	Aritmetico d;
	Bytes b;
	char bitN = 8;
	cantBytes byteN = 3;
	char c = 0x69;
	b.agregarAlFinal(&c, 1);
	c = 0x92;
	b.agregarAlFinal(&c, 1);
	c = 0xc0;
	b.agregarAlFinal(&c, 1);
	c = 0x1e;
	b.agregarAlFinal(&c, 1);
	c = 0xf0;
	b.agregarAlFinal(&c, 1);
	uint32 ventana = 0;
	b.leer(&ventana, 0, 4);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n", cambiarO(ventana));

	d.moverVentanaNbits(ventana, 1, b, byteN, bitN, CASO_UNDERFLOW);
	printf("ventana: %#x \n", ventana);
	printf("ventanaC: %#x \n", cambiarO(ventana));
}

void TestCompresor::CASASOAS() {
	std::cout
			<< "--------------------------Test de comprimir y descomprimir CASASOAS-----------------------------\n"
			<< std::endl;

	char c = 0;

	Aritmetico aritmetico;
	Bytes entrada("CASASOAS");
	int tam = entrada.getTamanio();

	std::cout << "--------------Se comprime: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		entrada.leer(&c, i, 1);
		printf("%c", c);
	}

	std::cout << "\n--------------que actualmente tiene: " << tam
			<< " bytes. \n" << std::endl;

	Bytes salida = aritmetico.comprimir(entrada);
	tam = salida.getTamanio();

	std::cout << "--------------La salida al comprimir es: \n" << tam << endl;
	c = 0;
	for (unsigned i = 0; i < tam; ++i) {
		salida.leer(&c, i, 1);
		printf("%#x \n", c);
	}

	std::cout << "--------------------que mide: " << tam << " bytes.\n"
			<< std::endl;

	cantBytes cn =3;
	Bytes nulo = crearBytesNulos(cn);
	salida.agregarAlFinal(nulo);
	tam = salida.getTamanio();
	std::cout << "--------------------que mide: " << tam << " bytes.\n"
				<< std::endl;
	Aritmetico descompresor;
	Bytes emision = descompresor.descomprimir(salida);
	tam = emision.getTamanio();
	c = 0;

	std::cout << "--------------Se descomprime, obteniendose: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		emision.leer(&c, i, 1);
		printf("%c", c);
	}
	std::cout << "\n" << std::endl;
	assert(emision.toString() == entrada.toString(),
			" Lo obtenido es igual a la entrada del compresor. ");

}

void TestCompresor::Ab() {
	std::cout
			<< "--------------------------Test de comprimir y descomprimir Ab-----------------------------\n"
			<< std::endl;

	char c = 0;

	Aritmetico aritmetico;
	Bytes entrada("Ab");
	int tam = entrada.getTamanio();

	std::cout << "--------------Se comprime: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		entrada.leer(&c, i, 1);
		printf("%c", c);
	}

	std::cout << "\n--------------que actualmente tiene: " << tam
			<< " bytes. \n" << std::endl;

	Bytes salida = aritmetico.comprimir(entrada);
	tam = salida.getTamanio();

	std::cout << "--------------La salida al comprimir es: \n" << tam << endl;
	c = 0;
	for (unsigned i = 0; i < tam; ++i) {
		salida.leer(&c, i, 1);
		printf("%#x \n", c);
	}

	std::cout << "--------------------que mide: " << tam << " bytes.\n"
			<< std::endl;

	Aritmetico descompresor;
	Bytes emision = descompresor.descomprimir(salida);
	tam = emision.getTamanio();
	c = 0;

	std::cout << "--------------Se descomprime, obteniendose: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		emision.leer(&c, i, 1);
		printf("%c", c);
	}
	std::cout << "\n" << std::endl;
	assert(emision.toString() == entrada.toString(),
			" Lo obtenido es igual a la entrada del compresor. ");

}
void TestCompresor::AAA() {
	std::cout
			<< "--------------------------Test de comprimir y descomprimir AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-----------------------------\n"
			<< std::endl;

	char c = 0;

	Aritmetico aritmetico;
	Bytes entrada(
			"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
	int tam = entrada.getTamanio();

	std::cout << "--------------Se comprime: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		entrada.leer(&c, i, 1);
		printf("%c", c);
	}

	std::cout << "\n--------------que actualmente tiene: " << tam
			<< " bytes. \n" << std::endl;

	Bytes salida = aritmetico.comprimir(entrada);
	tam = salida.getTamanio();

	std::cout << "--------------La salida al comprimir es: \n" << tam << endl;
	c = 0;
	for (unsigned i = 0; i < tam; ++i) {
		salida.leer(&c, i, 1);
		printf("%#x \n", c);
	}

	std::cout << "--------------------que mide: " << tam << " bytes.\n"
			<< std::endl;

	Aritmetico descompresor;
	Bytes emision = descompresor.descomprimir(salida);
	tam = emision.getTamanio();
	c = 0;

	std::cout << "--------------Se descomprime, obteniendose: " << std::endl;
	for (unsigned i = 0; i < tam; ++i) {
		emision.leer(&c, i, 1);
		printf("%c", c);
	}
	std::cout << "\n" << std::endl;
	assert(emision.toString() == entrada.toString(),
			" Lo obtenido es igual a la entrada del compresor. ");

}

void TestCompresor::biblia() {

	std::cout << " --------------------Test: BIBLIA----------------------"
			<< std::endl;

	FILE* fp = fopen("BIBLIA.txt", "r");
	if (fp == NULL) {
		std::cerr << "No se pudo abrir el archivo de prueba" << std::endl;
		return;
	}
	Bytes entrada;
	char c;
	while ((c = fgetc(fp)) != EOF) {
		entrada.agregarAlFinal(&c, 1);
	}

	int tam = entrada.getTamanio();

	std::cout << "--------------Se comprime BIBLIA " << std::endl;

	std::cout << "\n--------------que actualmente tiene: " << tam
			<< " bytes. \n" << std::endl;
	Aritmetico aritmetico;
	Bytes salida = aritmetico.comprimir(entrada);
	tam = salida.getTamanio();

	std::cout << "--------------La salida al comprimir  " << tam << endl;
	std::cout << "-------------------- mide: " << tam << " bytes.\n"
			<< std::endl;

	Aritmetico descompresor;
	Bytes emision = descompresor.descomprimir(salida);
	tam = emision.getTamanio();
	c = 0;

	std::cout << "--------------Se descomprime, obteniendose: " << std::endl;
	std::cout << "-------------------- " << tam << " bytes.\n" << std::endl;

	std::cout << "\n" << std::endl;
	assert(emision.toString() == entrada.toString(),
			" Lo obtenido al descomprimir es igual a la entrada del compresor. ");
	fclose(fp);

}
